/*
	Copyright (c) 2008 TPSi
	Alex Iskander
	
	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

*/



#ifndef BINDABLE_BINDABLE_IBOUNDARRAY_H_
#define BINDABLE_BINDABLE_IBOUNDARRAY_H_

//template<typename ElementType> class IArrayChangeResponder;
#include "BindableArray.h"

namespace Bind
{
	/**
	 \class BoundArray
	 \brief BoundArray is a consumer of BindableArray.
	 
	 BoundArray is, itself, a BindableArray. However, it \e requires a 
	 BindableArray back-end to function.
	 */
	template<typename ElementType>
	class BoundArray : public BindableArray<ElementType>
	{
	public:
		/**
			\fn BoundArray();
			\brief Constructor.
		*/
		BoundArray();
		
		

		
		/**
			\fn getLength();
			\brief Returns the number of elements.
			
			If you were accessing a MySQL database as a back-end, you may return the result
			of a query like "SELECT COUNT(*) FROM MYTABLE".
			
			Optionally, you can return -1. This means that the length is unknown. It is best
			to avoid unknown lengths if possible, as it can cause complications in returning
			empty results.
		*/
		virtual long getLength();
		
		/**
		 \fn elementExists(long index);
		 \brief Returns whether an element exists at the supplied index.
		 
		 It is not required to implement this function. However, if there is any circumstance
		 in which getLength will return -1, this function \e should be implemented, as without
		 it, consumers can not determine if a specified element exists.
		 */
		virtual bool elementExists(long index);
		
		/**
			\fn getElement(long index);
			\brief Returns the element at the specified index.
			
			Implementers must handle nonexistent items gracefully by returning some form of
			blank value.
		*/
		virtual ElementType getElement(long index);

		
		
		/**
		 \fn ~BoundArray();
		 \brief Destructor.
		 */
		virtual ~BoundArray();
		
		
		using BindableArray<ElementType>::operator=;

	protected:
		/**
		 \fn onBind(Bindable *bindTo, bool copy);
		 \brief Binds BoundArray to the supplied BindableObject array.
		 */
		virtual void onBind(Bindable *bindTo, bool copy);
		
		/**
		 \fn onUnbind(Bindable *bindTo);
		 */
		virtual void onUnbind(Bindable *bindTo);
		
		
		
		BindableArray<ElementType> *link;
		//!< The array provider to which this is bound.
	};
	

	template<typename ElementType>
	BoundArray<ElementType>::BoundArray()
	{
		//initialize link to NULL.
		this->link = NULL;
	}


	template<typename ElementType> void
	BoundArray<ElementType>::onBind(Bindable *property, bool copy)
	{
		//set existing link to null.
		this->link = NULL;

		//get what we should bind to
		BindableArray<ElementType> *bindTo = dynamic_cast< BindableArray<ElementType> *> (property);
		
		//check for validity
		if (bindTo == this)
		{
			//this should throw an object...
			throw "Invalid bind.";
		}
		
		//if bindTo doesn't exist
		if (!bindTo)
		{
			//we really should throw some sort of error... but for now, we'll just return.
			//that don't work!
			return;
			
			//! @todo This should probably throw some sort of error to alert the caller
			//!			that something has gone wrong.
		}

		//set link to the property to bind to
		this->link = bindTo;
		
		//alert listeners
		this->update();
	}
	
	template<typename ElementType> void
	BoundArray<ElementType>::onUnbind(Bindable *property)
	{
		//unlink
		this->link = NULL;
	}
	
	template<typename ElementType> long
	BoundArray<ElementType>::getLength()
	{
		//if we are not bound to anything
		if (!this->link)
		{
			//return 0
			return 0;
		}
		
		//otherwise, return the length of whatever we are bound to
		return this->link->getLength();
	}
	
	
	template<typename ElementType> ElementType
	BoundArray<ElementType>::getElement(long index)
	{
		//if we are not bound to anything
		if (!this->link)
		{
			//return an empty object
			return ElementType();
		}
		
		//otherwise, return the item from whatever we're bound to
		return this->link->getElement(index);
	}
	
	template<typename ElementType> bool
	BoundArray<ElementType>::elementExists(long index)
	{
		//if we are not bound to anything
		if (!this->link)
		{
			//return false (how could it exist if there is no array?)
			return false;
		}
		
		//otherwise, return the linked array's answer
		return this->link->elementExists(index);
	}
	
	
	template<typename ElementType>
	BoundArray<ElementType>::~BoundArray()
	{

	}
}


#endif
